#include <cnet.h>
#include <string.h>
#include <stdint.h>
#include <math.h>

#define min(x,y) ((x)<=(y)?(x):(y))

typedef struct{
	char data[MAX_MESSAGE_SIZE]; /* 10240 bytes */
} MSG; /* data type for the unsplit message that comes directly from the application */


static CnetTimerID		lasttimer	= NULLTIMER; /* each node has its own timer */
static size_t			len		= MAX_MESSAGE_SIZE, /* actual message length */
						rcv_len 	= 0; /* length of the message received so far */
static 					uint8_t			parts, /* the number of frame parts to send */
						rcv_parts 	= 0; /* number of expected parts */
static CnetAddr 		dest; /* the destination of the message */
static MSG				msg; /* the unsplit message */
static char*			ptr		= msg.data; /* the pointer to the character to be sent next */

EVENT_HANDLER(application_ready){
	CHECK(CNET_read_application(&dest,(char *)&msg,&len));/* read in data from application */
	CHECK(CNET_disable_application(dest)); /* prevent application from generating other messages for the same destination */
	lasttimer = CNET_start_timer(EV_TIMER1, 1, 0); /* start timer immediately*/
}

static void timeouts(CnetEvent ev, CnetTimerID timer, CnetData data)
{
	size_t flen = (size_t)96; /* the length of the frame */
	CnetTime timeout = 768; /* no need to recompute the same thing over and over again */
	switch (data) {
		case -1: /* MESSAGE DELIVERED. NEXT PLEASE */
			CHECK(CNET_enable_application(dest)); /* allow the application to send more messages */
			return;
		case 0: /* SETUP FRAME */
			parts = (uint8_t)ceil(len/96.0); /* compute the number of parts */
			lasttimer = CNET_start_timer(EV_TIMER1, (CnetTime)(9), (CnetData)parts); /* start timer */
			size_t ONEBYTE = 1;
			CHECK(CNET_write_physical(1,&parts,&ONEBYTE)); /* write the setup byte onto the link */
			return;
		case 1: /* LAST FRAME */
			timeout = len*(CnetTime)8000000 / linkinfo[1].bandwidth; /* compute transmission delay */
			CHECK(CNET_write_physical(1,ptr,&len)); /* write the last frame onto the link */
			len = MAX_MESSAGE_SIZE; /* reset maximum length */
			ptr = msg.data; /* reset current pointer */
			lasttimer = CNET_start_timer(EV_TIMER1, (CnetTime)(timeout+1), (CnetData)-1); /* start timer again */
			return;
	}
	/* NORMAL FRAME */
	size_t tocopy = min(len,96); /* compute the number of bytes to copy */
	lasttimer = CNET_start_timer(EV_TIMER1, (CnetTime)(timeout+1), (CnetData)--parts); /* start timer again */
	CHECK(CNET_write_physical(1,ptr,&flen)); /* write the frame onto the link */
	len-=tocopy; /* update untransmitted byte count */
	ptr+=tocopy; /* update current message pointer */
}

EVENT_HANDLER(physical_ready){
	static MSG 	rcv_msg; /* reserve space for the received message */
	static char*	rcv_ptr = NULL; /* current pointer */
	size_t 		prt_len	= 96; /* length of the current part */
	int		link	= 1;
	switch (rcv_parts) {
		case 0: /* RECEIVING SETUP FRAME */
			CHECK(CNET_read_physical(&link, &rcv_parts, &prt_len)); /* read in setup information */
			rcv_ptr = rcv_msg.data; /* reset buffer pointer */
			rcv_len = 0; /* reset message length */
			return;
		case 1: /* RECEIVING LAST FRAME */
			CHECK(CNET_read_physical(&link, rcv_ptr, &prt_len)); /* read in last message fragment */
			rcv_len+=prt_len; /* add up overall length */
			rcv_parts = 0; /* decrease number of expected parts */
			CHECK(CNET_write_application(rcv_msg.data, &rcv_len)); /* forward data to application */
			return;
		default: /* RECEIVING NORMAL FRAME */
			CHECK(CNET_read_physical(&link, rcv_ptr, &prt_len)); /* read in message fragment */
			rcv_len+=prt_len; /* add up overall length */
			rcv_ptr+=prt_len; /* update current pointer */
			rcv_parts--; /* decrease number of expected parts */
	}
}

EVENT_HANDLER(reboot_node){
	CHECK(CNET_set_handler(EV_APPLICATIONREADY, application_ready, 0));
	CHECK(CNET_set_handler(EV_PHYSICALREADY, physical_ready, 0));
	CHECK(CNET_set_handler(EV_TIMER1, timeouts, 0));
	CHECK(CNET_enable_application(ALLNODES));
}
